/**
 * $version:  0.1 
 * $Date: 2011-01-21 
 *
 * Copyright (C) 2010-2011 Jawa Software. All rights reserved.
 *
 */
package org.jawa.core.database;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

import org.jawa.core.util.JawaGlobals;
import org.logicalcobwebs.proxool.ConnectionPoolDefinitionIF;
import org.logicalcobwebs.proxool.ProxoolException;
import org.logicalcobwebs.proxool.ProxoolFacade;
import org.logicalcobwebs.proxool.admin.SnapshotIF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 默认的数据库连接池提供程序.
 * 
 * @author Y-N
 */
public class DefaultConnectionProvider implements ConnectionProvider {

	private static final Logger Log = LoggerFactory
			.getLogger(DefaultConnectionProvider.class);

	private Properties settings;
	private String driver;
	private String serverURL;
	private String proxoolURL;
	private String username;
	private String password;

	// 最小的数据库连接数
	private int minConnections = 3;

	// 最大的数据库连接数
	private int maxConnections = 10;

	// 如果housekeeper 检测到某个线程的活动时间大于这个数值.它将会杀掉这个数据库连接.默认是15分钟
	private int activeTimeout = 900000;

	// 测试数据库连接的 SQL
	private String testSQL = "";

	// 如果为true，在每个连接被使用前都会进行测试，如果一个连接测试失败，那么将被丢弃，另一个连接将会被处理，如果所有连接都失败，一个新的连接将会被建立。否则将会抛出一个SQLException异常
	private Boolean testBeforeUse = true;

	// 如果为true，在每个连接被测试后都会进行测 试，使其回到连接池中，如果连接测试失败，那么将被废弃
	private Boolean testAfterUse = true;

	// 一个数据库连接的最大寿命(单位是天)
	private double connectionTimeout = 0.5;

	/**
	 * 创建一个默认的数据库连接池提供程序.
	 */
	public DefaultConnectionProvider() {
		loadProperties();
	}

	public boolean isPooled() {
		return true;
	}

	public Connection getConnection() throws SQLException {
		try {
			Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
			return DriverManager.getConnection(proxoolURL, settings);
		} catch (ClassNotFoundException e) {
			throw new SQLException("DbConnectionProvider: 接不到驱动程序: " + e);
		}
	}

	public void start() {
		proxoolURL = "proxool.jawa:" + getDriver() + ":" + getServerURL();
		settings = new Properties();
		settings.setProperty("proxool.maximum-activetime", Integer
				.toString(activeTimeout));
		settings.setProperty("proxool.maximum-connection-count", Integer
				.toString(getMaxConnections()));
		settings.setProperty("proxool.minimum-connection-count", Integer
				.toString(getMinConnections()));
		settings.setProperty("proxool.maximum-connection-lifetime", Integer
				.toString((int) (86400000 * getConnectionTimeout())));
		settings.setProperty("proxool.test-before-use", testBeforeUse
				.toString());
		settings.setProperty("proxool.test-after-use", testAfterUse.toString());
		settings.setProperty("proxool.house-keeping-test-sql", testSQL);
		settings.setProperty("user", getUsername());
		settings.setProperty("password", (getPassword() != null ? getPassword()
				: ""));
	}

	public void restart() {
	}

	public void destroy() {
		settings = null;
		ProxoolFacade.shutdown(0);
	}

	/**
	 * 为数据库连接池返回一个JDBC 驱动名称.例如: com.mysql.jdbc.Driver
	 * 
	 * @return JDBC 驱动的全称.
	 */
	public String getDriver() {
		return driver;
	}

	/**
	 * 为数据库连接池设置一个JDBC 驱动名称.例如: com.mysql.jdbc.Driver
	 * 
	 * @param driver
	 *            驱动的全称.
	 */
	public void setDriver(String driver) {
		this.driver = driver;
		saveProperties();
	}

	/**
	 * 返回数据库连接池的JDBC URL .
	 * 
	 * @return 数据库 JDBC URL.
	 */
	public String getServerURL() {
		return serverURL;
	}

	/**
	 * 设置数据库连接池的JDBC URL .
	 * 
	 * @param serverURL
	 *            JDBC URL.
	 */
	public void setServerURL(String serverURL) {
		this.serverURL = serverURL;
		saveProperties();
	}

	/**
	 * 返回数据库的用户名.
	 * 
	 * @return 数据库的用户名.
	 */
	public String getUsername() {
		return username;
	}

	/**
	 *设置数据库的用户名.
	 * 
	 * @param username
	 *            数据库的用户名.
	 */
	public void setUsername(String username) {
		this.username = username;
		saveProperties();
	}

	/**
	 * 返回数据库的密码.
	 * 
	 * @return 数据库的密码.
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * 设置数据库的密码.
	 * 
	 * @param password
	 *            数据库的密码.
	 */
	public void setPassword(String password) {
		this.password = password;
		saveProperties();
	}

	/**
	 * 返回数据库连接池将使用的最低数据库连接数量,正常情况下应该至少3个.
	 * 
	 * @return 数据库连接池最低数量.
	 */
	public int getMinConnections() {
		return minConnections;
	}

	/**
	 * 设置数据库连接池将使用的最低数据库连接数量,正常情况下应该至少3个.
	 * 
	 * @param minConnections
	 *            数据库连接池最低数量.
	 */
	public void setMinConnections(int minConnections) {
		this.minConnections = minConnections;
		saveProperties();
	}

	/**
	 * 返回数据库连接池将使用的最大数据库连接数量.
	 * 
	 * @return 最大数据库连接数量.
	 */
	public int getMaxConnections() {
		return maxConnections;
	}

	/**
	 * 设置数据库连接池将使用的最大数据库连接数量.
	 * 
	 * @param maxConnections
	 *            最大数据库连接数量.
	 */
	public void setMaxConnections(int maxConnections) {
		this.maxConnections = maxConnections;
		saveProperties();
	}

	/**
	 * 返回一个数据库连接多长时间后将被回收,单位是天.例如:一个0.5的相当于半天.
	 * 
	 * @return 数据库连接超时被回收的天数.
	 */
	public double getConnectionTimeout() {
		return connectionTimeout;
	}

	/**
	 * 设置一个数据库连接多长时间后将被回收,单位是天.例如:一个0.5的相当于半天.
	 * 
	 * @param connectionTimeout
	 *            数据库连接超时被回收的天数.
	 */
	public void setConnectionTimeout(double connectionTimeout) {
		this.connectionTimeout = connectionTimeout;
		saveProperties();
	}

	/**
	 * 返回用于测试数据库是否正常的SQL.
	 * 
	 * @return 测试数据库是否正常的SQL.
	 */
	public String getTestSQL() {
		return testSQL;
	}

	/**
	 * 设置SQL语句用来测试一个连接是否有效.House keeping 和 testbefore/testafter
	 * 将使用这个功能,他应该是一个效率很高,很快并且不占数据库大量工作的一个SQL.
	 * 
	 * @param testSQL
	 *            数据库测试语句.
	 */
	public void setTestSQL(String testSQL) {
		this.testSQL = testSQL;
	}

	/**
	 * 返回是否每个连接在使用前都需要被测试.
	 * 
	 * @return 如果为 true 每个连接在使用前都会被测试.
	 */
	public Boolean getTestBeforeUse() {
		return testBeforeUse;
	}

	/**
	 * 设置是否每个连接在使用前都需要被测试.
	 * 
	 * @param testBeforeUse
	 *            如果为 true 每个连接在使用前都会被测试.
	 */
	public void setTestBeforeUse(Boolean testBeforeUse) {
		this.testBeforeUse = testBeforeUse;
	}

	/**
	 * 返回是否每个连接在使用后都需要被测试.
	 * 
	 * @return 如果为 true 每个连接在使用后都会被测试.
	 */
	public Boolean getTestAfterUse() {
		return testAfterUse;
	}

	/**
	 * 设置是否每个连接在使用后都需要被测试.
	 * 
	 * @param testAfterUse
	 *            如果为 true 每个连接在使用后都会被测试.
	 */
	public void setTestAfterUse(Boolean testAfterUse) {
		this.testAfterUse = testAfterUse;
	}

	/**
	 * 加载数据库连接池属性.
	 */
	private void loadProperties() {

		driver = JawaGlobals
				.getXMLProperty("database.defaultProvider.driver");
		serverURL = JawaGlobals
				.getXMLProperty("database.defaultProvider.serverURL");
		username = JawaGlobals
				.getXMLProperty("database.defaultProvider.username");
		password = JawaGlobals
				.getXMLProperty("database.defaultProvider.password");
		String minCons = JawaGlobals
				.getXMLProperty("database.defaultProvider.minConnections");
		String maxCons = JawaGlobals
				.getXMLProperty("database.defaultProvider.maxConnections");
		String conTimeout = JawaGlobals
				.getXMLProperty("database.defaultProvider.connectionTimeout");
		testSQL = JawaGlobals.getXMLProperty(
				"database.defaultProvider.testSQL", DbConnectionManager
						.getTestSQL(driver));
		testBeforeUse = JawaGlobals.getXMLProperty(
				"database.defaultProvider.testBeforeUse", true);
		testAfterUse = JawaGlobals.getXMLProperty(
				"database.defaultProvider.testAfterUse", true);

		try {
			if (minCons != null) {
				minConnections = Integer.parseInt(minCons);
			}
			if (maxCons != null) {
				maxConnections = Integer.parseInt(maxCons);
			}
			if (conTimeout != null) {
				connectionTimeout = Double.parseDouble(conTimeout);
			}
		} catch (Exception e) {
			Log.error("错误: 无法解析默认的数据库连接池属性. " + "请确认数据库连接池的属性在配制文件中已配制.", e);

		}
	}

	/**
	 * 保存数据库连接池属性.
	 */
	private void saveProperties() {
		JawaGlobals.setXMLProperty("database.defaultProvider.driver", driver);
		JawaGlobals.setXMLProperty("database.defaultProvider.serverURL",
				serverURL);
		JawaGlobals.setXMLProperty("database.defaultProvider.username",
				username);
		JawaGlobals.setXMLProperty("database.defaultProvider.password",
				password);
		JawaGlobals.setXMLProperty("database.defaultProvider.testSQL",
				testSQL);
		JawaGlobals.setXMLProperty("database.defaultProvider.testBeforeUse",
				testBeforeUse.toString());
		JawaGlobals.setXMLProperty("database.defaultProvider.testAfterUse",
				testAfterUse.toString());

		JawaGlobals.setXMLProperty("database.defaultProvider.minConnections",
				Integer.toString(minConnections));
		JawaGlobals.setXMLProperty("database.defaultProvider.maxConnections",
				Integer.toString(maxConnections));
		JawaGlobals.setXMLProperty(
				"database.defaultProvider.connectionTimeout", Double
						.toString(connectionTimeout));
	}

	public String toString() {
		try {
			ConnectionPoolDefinitionIF poolDef = ProxoolFacade
					.getConnectionPoolDefinition("jawa");
			SnapshotIF poolStats = ProxoolFacade.getSnapshot("jawa", true);
			return poolDef.getMinimumConnectionCount() + ","
					+ poolDef.getMaximumConnectionCount() + ","
					+ poolStats.getAvailableConnectionCount() + ","
					+ poolStats.getActiveConnectionCount();
		} catch (ProxoolException e) {
			return "默认数据库连接池.";
		}
	}
}
